﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using log4net;
using Harkon.AppServer.cOne.Registry;
using Castle.Core.Interceptor;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.Runtime.Serialization;
using System.Xml;
using System.IO;

namespace Harkon.AppServer.cOne.Service.Remote
{
    public class RemoteServiceProxy : IInterceptor
    {
        private static ILog logger = LogManager.GetLogger(typeof(RemoteServiceProxy));

        public IRegistry Registry { get; set; }

        public ServiceName WrappedServiceName { get; set; }

        public string RemoteServiceUri { get; set; }

        private bool initialized = false;
        private IRemoteCall targetMux = null;

        public RemoteServiceProxy(ServiceName wrappedServiceName)
        {
            WrappedServiceName = wrappedServiceName;
        }

        #region IInterceptor

        public void Intercept(IInvocation invocation)
        {
            if (invocation.Method.Name.Contains("Init") || invocation.Method.Name.Contains("Start") || invocation.Method.Name.Contains("Stop"))
                return;

            if (!initialized)
            {
                try
                {
                    RemoteServiceUri = Registry.GetServiceUri(WrappedServiceName);
                }
                catch (CommunicationException ex)
                {
                    logger.Error(string.Format("Can't establish channel for service {0} in component {1}", WrappedServiceName.Contract, WrappedServiceName.Component), ex);
                    return;
                }
                //Retrieve correct service name from the remote registry
                WrappedServiceName = Registry.GetCorrectServiceName(WrappedServiceName);
                //Establish channel to the target mux core remote service
                BasicHttpBinding binding = new BasicHttpBinding();
                EndpointAddress address = new EndpointAddress(RemoteServiceUri);
                logger.Info(string.Format("Establishing channel to mux on component {0} with uri {1}", WrappedServiceName.Component, RemoteServiceUri));
                targetMux = ChannelFactory<IRemoteCall>.CreateChannel(binding, address);

                initialized = true;
            }

            using (OperationContextScope scope = new OperationContextScope(targetMux as IContextChannel))
            {
                MessageHeader<string> beanName = new MessageHeader<string>(WrappedServiceName.Name);
                MessageHeader untypedBeanName = beanName.GetUntypedHeader("Bean", "ns");
                OperationContext.Current.OutgoingMessageHeaders.Add(untypedBeanName);

                MessageHeader<string> methodName = new MessageHeader<string>(invocation.Method.Name);
                MessageHeader untypedMethodName = methodName.GetUntypedHeader("Method", "ns");
                OperationContext.Current.OutgoingMessageHeaders.Add(untypedMethodName);

                logger.Info(string.Format("Calling method {0} on bean {1}", invocation.Method.Name, WrappedServiceName.Name));
                try
                {
                    string[] xmlParameters = Serialize(invocation.Arguments);
                    invocation.ReturnValue = targetMux.Execute(xmlParameters);
                }
                catch (CommunicationException e)
                {
                    logger.Error(string.Format("Can't call {0} on contract {1} in component {2} with target URL {3}",
                        invocation.Method.Name, WrappedServiceName.Contract, WrappedServiceName.Component, RemoteServiceUri), e);
                    initialized = false;
                }
            }
        }

        private string[] Serialize(object[] parameters)
        {
            if (parameters == null || parameters.Length == 0)
                return null;

            string[] ret = new string[parameters.Length];

            for (int index = 0; index < parameters.Length; index++)
            {
                object obj = parameters[index];
                StringBuilder builder = new StringBuilder();
                StringWriter stringWriter = new StringWriter(builder);
                XmlTextWriter xmlWriter = new XmlTextWriter(stringWriter);
                //XmlObjectSerializer serializer = new DataContractSerializer(obj.GetType(), null, Int32.MaxValue, false, false, null, contractResolver);
                XmlObjectSerializer serializer = new DataContractSerializer(obj.GetType());
                serializer.WriteObject(xmlWriter, obj);
                ret[index] = builder.ToString();
            }

            return ret;
        }

        #endregion
    }
}
